<?php
namespace App\Common\Cla\EChat\Room;

use App\Common as com;
use App\Common\Cla\EChat\Room\Msg as thisMsg;
use App\Common\Cla\EChat as nameEChat;

class Msg extends com\Cla\ClaBase {

	/**
	 * 消息类型->sys
	 * @var int
	 */
	public static int $varMsgType_Sys = 1;


	/**
	 * 消息类型->消息
	 * @var int
	 */
	public static int $varMsgType_Msg = 2;

	/**
	 * 回复类型->不需要
	 * @var int
	 */
	public static int $varReplyType_None = 0;

	/**
	 * 回复类型->单条
	 * @var int
	 */
	public static int $varReplyType_Single = 1;

	/**
	 * 回复类型->多条
	 * @var int
	 */
	public static int $varReplyType_Many = 2;

	/**
	 * @var int
	 */
	public static int $varHeaderReplyStatus_End = 2;

	/**
	 * 消息接收时间
	 * @var int
	 */
	private int $msgReceiveTime;

	private nameEChat\Room $roomObj;

	private \PhpAmqpLib\Message\AMQPMessage $AMQPMsg;

	/**
	 * AMQP数据（数组形式）
	 * @var array|null
	 */
	private array|null $AMQPMsgBodyArray;

	/**
	 * 回复房间是否存在
	 * @var bool
	 */
	private bool $replyRoomActivityIsExist;

	/**
	 * toMail结构
	 * @var array
	 */
	private array $toMailStruct;

	public function __construct( nameEChat\Room $roomObj, \PhpAmqpLib\Message\AMQPMessage $AMQPMsg, array|null $AMQPMsgBodyArray = null ) {
		$this->roomObj = $roomObj;
		$this->AMQPMsg = $AMQPMsg;
		if( $AMQPMsgBodyArray !== null ){
			$this->AMQPMsgBodyArray = $AMQPMsgBodyArray;
		}
		//
		$this->replyRoomActivityIsExist = $this->isNeedReply();
		//
		$this->msgReceiveTime = com\Lib\Fun\Time::getTime();
		//
		parent::__construct();
	}


	public function getRoom(  ):nameEChat\Room {
		return $this->roomObj;
	}

	/**
	 * @return int
	 */
	public function getMsgReceiveTime(  ):int {
		return $this->msgReceiveTime;
	}

	public function getAMQPMsg(  ):\PhpAmqpLib\Message\AMQPMessage {
		return $this->AMQPMsg;
	}

	/**
	 * 获取AMQP数据(数组形式)
	 * @return array|null
	 */
	public function getAMQPMsgBodyArray(  ):array|null {
		if( ! isset( $this->AMQPMsgBodyArray ) ){
			$this->AMQPMsgBodyArray = json_decode( $this->getAMQPMsg()->getBody(), true );
		}
		//
		return $this->AMQPMsgBodyArray;
	}

	public function getEChat(  ):com\Cla\EChat {
		return $this->getRoom()->getEChat();
	}

	/**
	 * 是否需要回复
	 * @return bool
	 */
	public function isNeedReply(  ):bool {
		if( isset( $this->replyRoomActivityIsExist ) && $this->replyRoomActivityIsExist !== true ){
			// 房间已关闭，不需要回复
			return false;
		}
		return in_array( $this->getReplyType(), [ self::$varReplyType_Single, self::$varReplyType_Many ] );
	}

	/**
	 * 获取回复通道房间
	 * @return nameEChat\Room
	 * @throws com\Lib\Err\BaseException\Throw_
	 */
	public function goReplyMailId(  ):nameEChat\Room {
		if( ! $this->isNeedReply() ){
			throw com\Lib\Err\Exception::makeOfNew( '该消息不需要回复', com\Lib\Err\Code::$sysAssertError );
		}
		//
		$fromMailId = $this->getFromMailIdOrNull();
		if( empty( $fromMailId ) ){
			throw com\Lib\Err\Exception::makeOfNew( '缺少fromMailId', com\Lib\Err\Code::$sysAssertError );
		}
		//
	    return $this->goPool( __FUNCTION__, function() use ( $fromMailId ) {
	        return $this->getEChat()->joinRoomOfMail( $fromMailId );
	    } );
	}

/*	public function getChatId() {

	}*/

	/**
	 * 获取AMQP数据
	 * @param string|null $keyPath ['']返回数组
	 * @return int|mixed|string|null
	 */
	public function getAMQPMsgData( string $keyPath = null ):mixed {
		if( $keyPath === null ){
			return $this->getAMQPMsg()->getBody();
		}else if( $keyPath === '' ){
			return $this->getAMQPMsgBodyArray();
		}else{
			$AMQPMsgBodyArray = $this->getAMQPMsgBodyArray();
			if( empty( $AMQPMsgBodyArray ) ){
				return null;
			}
			return com\Lib\Fun\Array_::assignPathRead( $AMQPMsgBodyArray, $keyPath, [
				'default' => null,
			] );
		}
	}

	/**
	 * 获取头数据
	 * @param string|null $keyPath
	 * @param mixed $default 默认值
	 * @return int|mixed|string|null [null]不存在
	 */
	public function getHeaders( string $keyPath = null, mixed $default = null ):mixed {
		if( $keyPath === null ){
			return $this->getAMQPMsgData( 'headers' );
		}else{
			$headersArray = $this->getAMQPMsgData( 'headers' );
			return com\Lib\Fun\Array_::assignPathRead( $headersArray, $keyPath, [
				'default' => $default,
			] );
		}
	}

	/**
	 * 获取头数据->replyStatus
	 * @return int|null
	 */
	public function getReplyStatusOrNull():?int {
		return (int) $this->getHeaders( 'replyStatus' );
	}

	/**
	 * 是否为系统消息
	 * @return bool
	 */
	public function isMsgTypeAsSys(  ):bool {
		return $this->getMsgTypeOrNull() === self::$varMsgType_Sys;
	}

	/**
	 * 是否为mail消息
	 * @return bool
	 */
	public function isMailMsg(  ):bool {
		return $this->getFromMailIdOrNull() !== null;
	}

	/**
	 * 获取from mailId
	 * @return string|null
	 */
	public function getFromMailIdOrNull(  ):string|null {
		return $this->getHeaders( 'fromMailId' );
	}

	/**
	 * 备注：建议使用getMsgType，保留该方法原因是兼容旧代码
	 * 获取from mailId（默认为：self::$varMsgType_Msg）
	 * @return string|null
	 */
	public function getMsgTypeOrNull(  ):string|null {
		return $this->getHeaders( 'msgType', self::$varMsgType_Msg );
	}

	/**
	 * 获取from mailId（默认为：self::$varMsgType_Msg）
	 * @return int
	 */
	public function getMsgType(  ):int {
		return $this->getHeaders( 'msgType', self::$varMsgType_Msg );
	}

	/**
	 * 获取from mail结构
	 * @return array|null
	 */
	public function getFromMailStruct(  ):array|null {
		$mailId = $this->getFromMailIdOrNull();
		if( $mailId === null ){
			return null;
		}
		//
		return explode( '@', $mailId );
	}

	/**
	 * 获取to mailId
	 * @return string|null
	 */
	public function getToMailIdOrNull(  ):string|null {
		return $this->getHeaders( 'toMailId' );
	}

	/**
	 * 获取to mail结构
	 * @return array|null
	 */
	public function getToMailStruct(  ):array|null {
		if( ! isset( $toMailStruct ) ){
			$mailId = $this->getToMailIdOrNull();
			if( $mailId === null ){
				return null;
			}
			//
			$this->toMailStruct = explode( '@', $mailId );
		}
		//
		return $this->toMailStruct;
	}

	/**
	 * 是否回复类型
	 * @return int
	 */
	public function getReplyType(  ):int {
		return $this->getHeaders( 'replyType' ) ?? self::$varReplyType_None;
	}

	/**
	 * 获取消息内容
	 * @param string|null $keyPath ['']返回数组
	 * @return mixed [null]空
	 */
	public function getMsgBody( string $keyPath = null ):mixed {
		if( $keyPath === null ){
			return $this->getAMQPMsgData( 'body' );
		}else{
			$bodyArray = $this->getAMQPMsgData( 'body' );
			return com\Lib\Fun\Array_::assignPathRead( $bodyArray, $keyPath, [
				'default' => null,
			] );
		}
	}

	/**
	 * @param array|string|int|float $data 回复内容
	 * @param array $headers 头
	 * @return bool
	 */
	public function send( array|string|int|float $data, array $headers = [] ):bool {
		return $this->getRoom()->send( $data );
	}

	/**
	 * 回复房间生命周期的闭包
	 * @param \Closure $fn $fn():bool|null [false]退出 [true|null]继续执行
	 * @param int $callFnIntervalMs 每次循环执行间隔 [1][默认]
	 * @return void
	 */
	public function replyRoomActivityFn( \Closure $fn, int $callFnIntervalMs = 1 ):void {
		$waitChannel = new \Swoole\Coroutine\Channel();
		//
		go( function() use ( $fn, $waitChannel, $callFnIntervalMs ){
			while( true ){
				// 检查是否结束
				if( $this->replyRoomActivityIsExist !== true ){
					break;
				}
				//
				if( call_user_func( $fn ) === false ){
					break;
				}
				// 默认延迟1ms，防止死循环
				\Swoole\Coroutine::sleep( $callFnIntervalMs / 1000 );
			}
			$waitChannel->close();
		} );
		//
		$waitChannel->pop();
	}

	/**
	 * 回复流（可多次回复）
	 * @param array|string $data 回复内容
	 * @return bool
	 * @throws com\Lib\Err\BaseException\Throw_
	 */
	public function replyStream( $data ):bool {
		if( ! $this->isNeedReply() ){
			return false;
		}
		//
		return $this->goReplyMailId()->send( $data );
	}

	/**
	 * 退出回复房间
	 * @return bool
	 * @throws
	 */
	public function replyExit( array|string $data = '' ):bool {
		if( ! $this->isNeedReply() ){
			return false;
		}
		//
		$this->replyRoomActivityIsExist = false;
		unset( $this->exchange );
		//
		return true;
	}

	/**
	 * 回复结束并退出回复房间
	 * @param array|string $data 回复内容
	 * @return bool
	 * @throws com\Lib\Err\BaseException\Throw_
	 */
	public function replyEnd( array|string $data = '' ):bool {
		if( ! $this->isNeedReply() ){
			return false;
		}
		//
		$sendBool = $this->goReplyMailId()->send( $data, [
			'replyStatus' => self::$varHeaderReplyStatus_End,
		] );
		//
		if( $sendBool === true ){
			$this->replyExit();
		}

		//
		return $sendBool;
	}

	/**
	 * 回复结束
	 * @param int $errCode
	 * @param string $msg
	 * @param array|string|null $data
	 * @return bool
	 * @throws com\Lib\Err\BaseException\Throw_
	 */
	public function replyEndOfApiResponse( int $errCode = 0, string $msg = '', array|string|null $data = [] ):bool {
		//
		$response = com\Lib\Api\ResponseAsCode::common( $errCode, $msg, $data );
		//
		return $this->replyEnd( $response->toArray() );
	}

	/**
	 * 回复结束(成功)
	 * @param array|string|null $data
	 * @return bool
	 * @throws com\Lib\Err\BaseException\Throw_
	 */
	public function replyEndOfApiResponseSuccess( array|string|null $data = [] ):bool {
		return $this->replyEndOfApiResponse( com\Lib\Api::$varApiCode_Success, '', $data );
	}

	/**
	 * 回复结束(失败)
	 * @param int|null $code [null][默认]错误
	 * @param string $msg
	 * @return bool
	 * @throws com\Lib\Err\BaseException\Throw_
	 */
	public function replyEndOfApiResponseError( int|null $code = null, string $msg = '' ):bool {
		if( $code === null ){
			$code = com\Lib\Api::$varApiCode_Error;
		}
		return $this->replyEndOfApiResponse( $code, $msg, [] );
	}

	// ===============================
	//
	//
	// 下面为静态方法
	//
	//
	// ===============================

	/**
	 * @param nameEChat\Room $roomObj
	 * @param \PhpAmqpLib\Message\AMQPMessage $AMQPMsg
	 * @return $this
	 */
	public static function makeOfNew( nameEChat\Room $roomObj, \PhpAmqpLib\Message\AMQPMessage $AMQPMsg ):self {
	    return self::make( $roomObj, $AMQPMsg );
	}

}
